package com.miku.blog.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.miku.blog.domain.*;
import com.miku.blog.domain.pojo.LoginUser;
import com.miku.blog.domain.pojo.ResponseResult;
import com.miku.blog.domain.vo.PageVo;
import com.miku.blog.eume.HttpStatusEnum;
import com.miku.blog.service.OperationLogService;
import com.miku.blog.mapper.OperationLogMapper;
import com.miku.blog.service.ResourceService;
import com.miku.blog.utils.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author Miku
 * @description 针对表【tb_operation_log】的数据库操作Service实现
 * @createDate 2022-06-06 10:35:49
 */
@Service
public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLog>
        implements OperationLogService {

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ResourceService resourceService;

    @Override
    public ResponseResult deleteOperationLogs(Integer[] operationLogIdList) {
        if (!removeBatchByIds(Arrays.asList(operationLogIdList))) {
            return new ResponseResult(HttpStatusEnum.REMOVE_DATA_ERROR);
        }
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult getOperationLogs(Integer current, Integer size,@Nullable String keywords) {

        Page<OperationLog> page = new Page<>(current, size);
        LambdaQueryWrapper<OperationLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(keywords), OperationLog::getOptDesc, keywords)
                .or()
                .like(StringUtils.hasText(keywords), OperationLog::getOptModule, keywords)
                .orderByDesc(OperationLog::getCreateTime);
        page(page, queryWrapper);
        PageVo<OperationLog> pageVo = new PageVo<>(page.getTotal(), page.getRecords());
        return ResponseResult.ok().setData(pageVo);
    }



    @Override
    public void addSuccessOperationLog(ProceedingJoinPoint joinPoint, Object proceed) {
        HttpServletRequest request = HttpUtils.getHttpServletRequest();
        String requestURI = request.getRequestURI();
        String requestMethod = request.getMethod().toUpperCase();
        if (requestMethod.equalsIgnoreCase("GET")
                || !requestURI.startsWith("/admin")
                || requestURI.equals("/admin/users/password")
                || requestURI.equals("/admin/users/info")
        ) {
            return;
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        String requestMappingValue = getRequestMappingValue(methodSignature);
        requestMappingValue = checkRestFulMapping(requestURI,requestMappingValue);

        LambdaQueryWrapper<Resource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Resource::getUrl, requestMappingValue)
                .eq(Resource::getRequestMethod, requestMethod);
        Resource resource = resourceService.getOne(queryWrapper);

        if (Objects.isNull(resource)) {
            return;
        }

        queryWrapper.clear();
        queryWrapper.eq(Resource::getId, resource.getParentId());
        Resource parentResource = resourceService.getOne(queryWrapper);

        Map<String, String[]> parameterMap = request.getParameterMap();
        Object[] obj = null;
        if (Objects.isNull(parameterMap) || parameterMap.size() <= 0){
            obj = joinPoint.getArgs();
            for (int i = 0;i < obj.length;i++){
                if (Objects.nonNull(obj[i]) && MultipartFile.class.isAssignableFrom(obj[i].getClass())){
                    obj[i] = ((MultipartFile) obj[i]).getOriginalFilename();
                }
            }
        }

        String redisUserCacheKey = GenerateKeyUtils.getRedisUserCacheKey(SecurityUtils.getUserAuth().getId());
        String redisCacheKey = redisCache.getCacheObject(redisUserCacheKey);
        LoginUser loginUser = redisCache.getCacheObject(redisCacheKey);

        OperationLog operationLog = new OperationLog();

        operationLog.setRequestMethod(requestMethod)

                .setRequestParam(JSON.toJSONString(Objects.isNull(obj) ? parameterMap : obj))

                .setOptDesc(resource.getResourceName())

                .setOptModule(parentResource.getResourceName())

                .setOptMethod(getMethodNameAndParameters(methodSignature))

                .setOptType(getOptType(requestMethod))

                .setOptUrl(requestURI)

                .setResponseData(JSON.toJSONString(proceed))

                .setUserId(loginUser.getUserAuth().getId())

                .setNickname(loginUser.getUserInfo().getNickname())

                .setIpAddr(loginUser.getUserLoginInfo().getIpAddr())

                .setIpSource(loginUser.getUserLoginInfo().getIpSource())

                .setCreateTime(new Date());

        save(operationLog);

    }


    private String getRequestMappingValue(MethodSignature methodSignature) {
        Method method = methodSignature.getMethod();
        String requestMappingValue = null;
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            String simpleName = annotation.annotationType().getSimpleName();
            if (simpleName.endsWith("Mapping")) {
                simpleName = simpleName.toUpperCase();
                if (simpleName.startsWith("POST")) {

                    PostMapping postMapping = (PostMapping) annotation;
                    requestMappingValue = postMapping.value()[0];

                } else if (simpleName.startsWith("PUT")) {

                    PutMapping putMapping = (PutMapping) annotation;
                    requestMappingValue = putMapping.value()[0];

                } else if (simpleName.startsWith("DELETE")) {

                    DeleteMapping deleteMapping = (DeleteMapping) annotation;
                    requestMappingValue = deleteMapping.value()[0];

                } else if (simpleName.startsWith("REQUEST")) {

                    RequestMapping requestMapping = (RequestMapping) annotation;
                    requestMappingValue = requestMapping.value()[0];

                } else if (simpleName.startsWith("GET")) {

                    GetMapping getMapping = (GetMapping) annotation;
                    requestMappingValue = getMapping.value()[0];

                }
                break;
            }
        }

        Class<?> declaringClass = method.getDeclaringClass();
        RequestMapping annotation = declaringClass.getAnnotation(RequestMapping.class);
        if (Objects.nonNull(annotation)) {
            String controllerRequestMappingValue = annotation.value()[0];
            if (StringUtils.hasText(controllerRequestMappingValue)) {
                requestMappingValue = controllerRequestMappingValue + requestMappingValue;
            }
        }
        return requestMappingValue;
    }

    private String getMethodNameAndParameters(MethodSignature methodSignature) {
        StringBuilder stringBuilder
                = new StringBuilder()
                .append(methodSignature.getDeclaringTypeName())
                .append(".")
                .append(methodSignature.getName())
                .append("(")
                .append(JSON.toJSONString(methodSignature.getParameterNames()))
                .append(")");
        return stringBuilder.toString();
    }

    private String getOptType(String requestMethod) {
        String optType = null;
        if (requestMethod.equals("POST")) {
            optType = "添加/修改数据";
        } else if (requestMethod.equals("PUT")) {
            optType = "修改数据";
        } else if (requestMethod.equals("DELETE")) {
            optType = "删除数据";
        } else if (requestMethod.equals("GET")) {
            optType = "查询数据";
        }
        return optType;
    }


    public static String checkRestFulMapping(String requestURI,String requestMappingValue){
        if (!requestURI.equals(requestMappingValue)) {
            StringBuilder append = new StringBuilder()
                    .append(requestMappingValue, 0, requestMappingValue.indexOf("{"))
                    .append("*");
            requestMappingValue = append.toString();
        }
        return requestMappingValue;
    }

}






